package com.SinhalaSpellChecker;

import Model.StringList;
import View.ErrorDialog;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.TreeMap;

/**
 *
 * @author L.C.T Silva
 */
public class WordListHandler {

    private String path = WordListHandler.class.getProtectionDomain().getCodeSource().getLocation().getPath();
    private static WordListHandler handler=handler=new WordListHandler();      //create an instance of the word list handler class;

    /**
     * Class constructor which will get the path of the file that contains the word list.
     * This is in the.oxt file. When the file is given this way the user do not have to perform
     * any other operations before installing the extension.
     */
    WordListHandler() {
        if(System.getProperty("os.name").equals("Linux"))
        {
            path = path.replace("%20", " "); //replace the space
            path = path.replace("SinhalaSpellChecker.jar", "SinhalaWords.sdic");//The file name of the word list is added to the path by replacing the name of the jar file with the file name
        }
        else{
            path = path.replace("/", "\\");     //the forward slashes are replace by double back slashes to the path
            path = path.replaceFirst("\\\\", ""); //the first occurence of /// is removed
            path = path.replace("%20", " "); //replace the space
            path = path.replace("SinhalaSpellChecker.jar", "SinhalaWords.sdic");//The file name of the word list is added to the path by replacing the name of the jar file with the file name
        }
    }

    /**
     * Retuns a static object of this class, WordListHandler. This oject can be used in any class.
     * This mathod does not take any arguments.
     * This method returns the object immediately, whether or not the obejct is created.
     * @return WordListHandler object
     */
    public static WordListHandler getHandler(){
        return handler;
    }

    /**
     * Returns a tree map which contains the sinhala words mapped according to the first letter.
     * This method does not take any parameters.
     * Reads from the file with the extension ".sdic" in th given path and store the object as a tree map. The TreeMap which is returned can onky
     * have String for keys and ArrayList for values
     * @return TreeMap<String, ArrayList>
     * @throws FileNotFoundException, IOException, UnsupportedEncodingException, ClassNotFoundException
     * @see  TreeMap, ObjectInputStream, FileInputStream
     */
    public TreeMap<String, ArrayList> readWordFile() throws FileNotFoundException, IOException, UnsupportedEncodingException, ClassNotFoundException{
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream(path));
        TreeMap<String, ArrayList> map=(TreeMap<String, ArrayList>)ois.readObject();    //cast the object read from the file in to a TreeMap
        return map;
    }

    /**
     * Returns a boolean variable indicationg whether the given word exists in the TreeMap.
     * Get the ArrayList that is mapped to the first letter of "aWord". Check if the arrayList contains the word.
     * If there is a match return true and if there is no match return false.
     * @param map a TreeMap with wild card string and ArrayList
     * @param aWord the word to be checked.
     * @return      boolean true or false
     */
     public boolean isExist(TreeMap<String, ArrayList> map, String aWord){
         boolean isExist=false;
         ArrayList<String> words=map.get(String.valueOf(aWord.charAt(0)));  //get the ArrayList that contains the words starting with the first letter of "aWord"
         for(int i=0; i<words.size(); i++){
             String word=words.get(i);
             if(word.equals(aWord)){
                 isExist=true;
                 break;
             }
         }
         return isExist;
     }

     /*In this methods suggestions for the given word is identified and retured as a string array. In here it is assumend that the first letter of the word is correct*/
     /**
      * Retruns a string array containing the suggestions. If no suggestions are available returns a string
      * array with no elements. When checking for suggestions it is assumed that the first letter is correct.
      * By this assumption the range of words that has to be compared is reduced thus improving the performance.
      * Then for every position in *aWord* all the characters in the sinhala unicode array are added and checked
      * with the word list if the new word is a valid word. Then if no suggestions are found, letters are removed
      * from "aWord" and again checked with the word list for the validity.
      * @param map a TreeMap with wild card string and ArrayList
      * @param aWord the word to be checked.
      * @return string array containing the suggestions
      * @see #compareNewWords(java.util.ArrayList, java.lang.String, java.lang.String[], Model.StringList, int, int)
      * @see #suggestionsWithoutSomeLetters(java.util.ArrayList, java.lang.String, Model.StringList, int, int)
      */
     public String[] identifySuggestions(TreeMap<String, ArrayList> map, String aWord){
         String[] suggestions=null;
         StringList<String> suggestionList=new StringList<String>();
         String[] unicodeArray={"\u0d82", "\u0d83", "\u0d85", "\u0d86", "\u0d87", "\u0d87",         //this list contains all the sinhala letters in unicode
                "\u0d89", "\u0d8a","\u0d8b", "\u0d8c", "\u0d8d", "\u0d8e", "\u0d8f", "\u0d90",
                "\u0d91", "\u0d92", "\u0d93", "\u0d94", "\u0d95", "\u0d96", "\u0d9a", "\u0d9b",
                "\u0d9c", "\u0d9d", "\u0d9e", "\u0d9f", "\u0da0", "\u0da1", "\u0da2", "\u0da3",
                "\u0da4", "\u0da5", "\u0da6", "\u0da7", "\u0da8", "\u0da9", "\u0daa", "\u0dab",
                "\u0dac", "\u0dae", "\u0daf", "\u0db0", "\u0db1", "\u0db3", "\u0db4", "\u0db5",
                "\u0db6", "\u0db7", "\u0db8", "\u0db9", "\u0dba", "\u0dbb", "\u0dbd", "\u0dc0",
                "\u0dc1", "\u0dc2", "\u0dc3", "\u0dc4", "\u0dc5", "\u0dc6", "\u0dca", "\u0dcf",
                "\u0dd0", "\u0dd1", "\u0dd2", "\u0dd3", "\u0dd4", "\u0dd6", "\u0dd8", "\u0dd9",
                "\u0dda", "\u0ddb", "\u0ddc", "\u0dde", "\u0ddf", "\u0df2", "\u0df3", "\u0df4"};
         int lengthOfTheWord=aWord.length();        //length of the given word
         String firstCharacter=String.valueOf(aWord.charAt(0));     //first character of the given word
         suggestionsReplacingTheFirstLetter(map, firstCharacter, aWord, suggestionList);
         ArrayList<String> words=map.get(firstCharacter);           //get the ArrayList that is mapped to the first letter of "aWord"
         suggestionAddingTheFirstLettet(words, aWord, suggestionList);
         if(words!=null){                                           //if the ArrayList is not null
             compareNewWords(words, aWord, unicodeArray, suggestionList, lengthOfTheWord, lengthOfTheWord);     //create new words based on the unicode array and check if  they are valid words
            // suggestionsReplacingTheFirstLetter(map, firstCharacter, aWord, suggestionList);
             int suggestionsLength=suggestionList.size();      //get the number of suggestion
             if(suggestionsLength>0){
                 suggestions=new String[suggestionsLength];         //create a strinf array with the size of the number of suggestions
                 for(int l=0; l<suggestionsLength; l++){
                     suggestions[l]=suggestionList.get(l);          //add the words in the ArrayList to the string array
                 }
             }else if(suggestionsLength==0){
                 suggestionsWithoutSomeLetters(words, aWord, suggestionList, lengthOfTheWord, lengthOfTheWord);
               //  suggestionsReplacingTheFirstLetter(map, firstCharacter, aWord, suggestionList);
                 int suggestionSize=suggestionList.size();
                 suggestions=new String[suggestionSize];         //create a string array with the size of the number of suggestions
                 for(int l=0; l<suggestionSize; l++){
                     suggestions[l]=suggestionList.get(l);          //add the words in the ArrayList to the string array
                 }
             }
        }
         return suggestions;
     }

     /**Create new strings by adding all the unicode characters to all the places and check if the words are valid.
      * If it is valid take the word as a suggestion and add to an ArrayList. Only words which length is 3 letters
      * more or less than the original word is added as suggestions.
      * @param words ArrayList containing the list of words starting with a particular letter
      * @param aWord word as a string
      * @param unicodeArray string array which stores the unicode values
      * @param suggestionList StringList which will be used to store the suggestions
      * @param lengthOfTheWord the length word as an integer
      * @param lengthOfTheOriginalWord the length of the original word(the word typed by the user of OpenOffice) as an integer
      */
      private void compareNewWords(ArrayList<String> words, String aWord, String[] unicodeArray, StringList<String> suggestionList, int lengthOfTheWord, int lengthOfTheOriginalWord){
         for(int i=1; i<lengthOfTheWord; i++){                  //get the length of "aWord". this is the mispelled word typed by the user or a word created by the previous iteration
             String substringFirstPart=aWord.substring(0, i+1); //split the word in to two in different locations. this is the first half
             String substringSecondPart=aWord.substring(i+1);   //the second half when the word is split in to two
             for(int j=0; j<unicodeArray.length; j++){
                 String newWord=substringFirstPart+unicodeArray[j]+substringSecondPart; //add a characater from the unicode array
                 for(int k=0; k<words.size(); k++){             //Iterate through the words in the ArrayList which has words starting with the first letter of "aWord"
                     if(newWord.equals(words.get(k))&& newWord.length()<lengthOfTheOriginalWord+3 && newWord.length()>lengthOfTheOriginalWord-3){       //check if the word is in the array list and the word length is betweem +3 and -3 of the length of the "aWord"
                         suggestionList.add(newWord);               //add the word to the ArrayList which contains the suggestions
                         compareNewWords(words, newWord, unicodeArray, suggestionList, newWord.length(), lengthOfTheOriginalWord); //call this method again to split and add unicode words to the newly created word. This is used to find more than one spelling errors.
                     }
                 }
             }
         }
     }
     /*Create new words by removing letters from each position and check with the word list if the word is valid. If it is valid take the word as a suggestion*/
      /**
       * Create new words by removing letters from each position and check with the word list if the word is valid.
       * If it is valid take the word as a suggestion. If the length of the word is less than or equal to one, then
       * it is not taken as a suggestion.
       * @param words ArrayList containing the list of words starting with a particular letter
       * @param  aWord word as a string
       * @param suggestionList StringList which will be used to store the suggestions
       * @param lengthOfTheWord the length word as an integer
       * @param lengthOfTheOriginalWord the length of the original word(the word typed by the user of OpenOffice) as an integer
       */
      private void suggestionsWithoutSomeLetters(ArrayList<String> words, String aWord, StringList<String> suggestionList, int lengthOfTheWord, int lengthOfTheOriginalWord){
          for(int i=0; i<lengthOfTheWord; i++){                 //get the length of "aWord".this is the mispelled word typed by the user or a word created by the previous iteration
             String substringFirstPart=aWord.substring(0, i); //split the word in to two in different locations. this is the first half
             String substringSecondPart=aWord.substring(i+1);   //the second half when the word is split in to two
             String newWord=substringFirstPart+substringSecondPart;
             for(int k=0; k<words.size(); k++){             //Iterate through the words in the ArrayList which has words starting with the first letter of "aWord"
                if(newWord.equals(words.get(k)) && newWord.length()>1){       //check if the word is in the array list and the word length is betweem +3 and -3 of the length of the "aWord"
                    suggestionList.add(newWord);               //add the word to the ArrayList which contains the suggestions
                    suggestionsWithoutSomeLetters(words, newWord, suggestionList, newWord.length(), lengthOfTheOriginalWord);
                }
            }
          }
      }

      private void suggestionsReplacingTheFirstLetter(TreeMap<String, ArrayList> map, String firstCharacter, String aword, StringList<String> suggestionList){
          String[][] firstLetterMapping={{"\u0d9a", "\u0d9b"}, {"\u0d9c", "\u0d9d"}, {"\u0da0", "\u0da1"},
          {"\u0da7", "\u0da8"}, {"\u0da9", "\u0daa"},{"\u0db1", "\u0dab"}, {"\u0dad", "\u0dae"},
          {"\u0daf", "\u0db0"},{ "\u0db4", "\u0db5"}, {"\u0db6", "\u0db7"}, {"\u0dbd", "\u0dc5"},
          { "\u0dc1", "\u0dc2"}};
          String mappingCharacterForFirstCharacter=null;
          for(int i=0; i<firstLetterMapping.length; i++){
              if(firstLetterMapping[i][0].equals(firstCharacter)){
                  mappingCharacterForFirstCharacter=firstLetterMapping[i][1];
                  break;
              }
              else if(firstLetterMapping[i][1].equals(firstCharacter)){
                  mappingCharacterForFirstCharacter=firstLetterMapping[i][0];
                  break;
              }
          }
          if(mappingCharacterForFirstCharacter!=null){
             String replaceTheFirstCharacterWithTheMappingCharacter=mappingCharacterForFirstCharacter+aword.substring(1);
             ArrayList<String> words=map.get(mappingCharacterForFirstCharacter);

            for(int j=0; j<words.size(); j++){
                String word=words.get(j);
                if(replaceTheFirstCharacterWithTheMappingCharacter.equals(word))
                {
                     suggestionList.add(replaceTheFirstCharacterWithTheMappingCharacter);
                     break;
                }
            }
          }
      }

      private void suggestionAddingTheFirstLettet(ArrayList<String> list, String aword, StringList<String> suggestionList){
           String[] parts={"\u0dca", "\u0dcf",
                "\u0dd0", "\u0dd1", "\u0dd2", "\u0dd3", "\u0dd4", "\u0dd6", "\u0dd8", "\u0dd9",
                "\u0dda", "\u0ddb", "\u0ddc", "\u0dde", "\u0ddf", "\u0df2", "\u0df3", "\u0df4"};
           String newWord=null;
           for(int i=0; i<parts.length; i++)
           {
                newWord=aword.substring(0, 1)+parts[i]+aword.substring(1);
                if(list.contains(newWord))
                {
                    suggestionList.add(newWord);
                }
           }
       }
}
